#include "HEMUIRCode.h"
#include "HEMUIRParse.h"
#include "HEMUMem.h"
#include "HEMUOp.h"

const std::string lr_delim = " \t,[]():{";

extern HEMUIRFTb func_tb;
extern HEMUIRFInsTb func_inst_tb;
extern HEMUIRGloTb glo_tb;
extern HEMUStack stack;
extern HEMUGMem gmem;
extern HEMUIRCode hemu_code;

//HEMUIRVar::HEMUIRVar() {
//	btype = HEMUIRBtype::UNKNOWN;
//}
//
//HEMUIRVar::HEMUIRVar(HEMUIRBtype _bt, HEMUIRVal _val) {
//	btype = _bt;
//	val = _val;
//}

HEMUIRVar HEMUIRVar::operator+(const HEMUIRVar& var) {
	HEMUIRVar res;
	if (btype == HEMUIRBtype::POINTER) {
		hemu_code.err(var.btype == HEMUIRBtype::I32, "err: HEMUIRVar op '+' type err!");
		res.btype = HEMUIRBtype::POINTER;
		res.val.ptr_val = val.ptr_val + var.val.i32_val;
	}
	else if (btype == HEMUIRBtype::I32) {
		hemu_code.err(var.btype == HEMUIRBtype::I32, "err: HEMUIRVar op '+' type err!");
		res.btype = HEMUIRBtype::I32;
		res.val.i32_val = val.i32_val + var.val.i32_val;
	}
	else if (btype == HEMUIRBtype::FLOAT) {
		hemu_code.err(var.btype == HEMUIRBtype::FLOAT, "err: HEMUIRVar op '+' type err!");
		res.btype = HEMUIRBtype::FLOAT;
		res.val.float_val = val.float_val + var.val.float_val;
	}
	else if (btype == HEMUIRBtype::DOUBLE) {
		hemu_code.err(var.btype == HEMUIRBtype::DOUBLE, "err: HEMUIRVar op '+' type err!");
		res.btype = HEMUIRBtype::DOUBLE;
		res.val.double_val = val.double_val + var.val.double_val;
	}
	else if (btype == HEMUIRBtype::I8) {
		hemu_code.err(var.btype == HEMUIRBtype::I8, "err: HEMUIRVar op '+' type err!");
		res.btype = HEMUIRBtype::I8;
		res.val.i8_val = val.i8_val + var.val.i8_val;
	}
	else {
		hemu_code.err(0, "err: HEMUIRVar op '+' type err!");
	}
	return res;
}

HEMUIRVar HEMUIRVar::operator-(const HEMUIRVar& var) {
	HEMUIRVar res;
	if (btype == HEMUIRBtype::I32) {
		hemu_code.err(var.btype == HEMUIRBtype::I32, "err: HEMUIRVar op '-' type err!");
		res.btype = HEMUIRBtype::I32;
		res.val.i32_val = val.i32_val - var.val.i32_val;
	}
	else if (btype == HEMUIRBtype::FLOAT) {
		hemu_code.err(var.btype == HEMUIRBtype::FLOAT, "err: HEMUIRVar op '-' type err!");
		res.btype = HEMUIRBtype::FLOAT;
		res.val.float_val = val.float_val - var.val.float_val;
	}
	else if (btype == HEMUIRBtype::DOUBLE) {
		hemu_code.err(var.btype == HEMUIRBtype::DOUBLE, "err: HEMUIRVar op '-' type err!");
		res.btype = HEMUIRBtype::DOUBLE;
		res.val.double_val = val.double_val - var.val.double_val;
	}
	else {
		hemu_code.err(0, "err: HEMUIRVar op '-' type err!");
	}
	return res;
}

HEMUIRVar HEMUIRVar::operator*(const HEMUIRVar& var) {
	HEMUIRVar res;
	if (btype == HEMUIRBtype::I32) {
		hemu_code.err(var.btype == HEMUIRBtype::I32, "err: HEMUIRVar op '*' type err!");
		res.btype = HEMUIRBtype::I32;
		res.val.i32_val = val.i32_val * var.val.i32_val;
	}
	else if (btype == HEMUIRBtype::FLOAT) {
		hemu_code.err(var.btype == HEMUIRBtype::FLOAT, "err: HEMUIRVar op '*' type err!");
		res.btype = HEMUIRBtype::FLOAT;
		res.val.float_val = val.float_val * var.val.float_val;
	}
	else if (btype == HEMUIRBtype::DOUBLE) {
		hemu_code.err(var.btype == HEMUIRBtype::DOUBLE, "err: HEMUIRVar op '*' type err!");
		res.btype = HEMUIRBtype::DOUBLE;
		res.val.double_val = val.double_val * var.val.double_val;
	}
	else {
		hemu_code.err(0, "err: HEMUIRVar op '*' type err!");
	}
	return res;
}

HEMUIRVar HEMUIRVar::operator/(const HEMUIRVar& var) {
	HEMUIRVar res;
	if (btype == HEMUIRBtype::I32) {
		hemu_code.err(var.btype == HEMUIRBtype::I32, "err: HEMUIRVar op '/' type err!");
		res.btype = HEMUIRBtype::I32;
		res.val.i32_val = val.i32_val / var.val.i32_val;
	}
	else if (btype == HEMUIRBtype::FLOAT) {
		hemu_code.err(var.btype == HEMUIRBtype::FLOAT, "err: HEMUIRVar op '/' type err!");
		res.btype = HEMUIRBtype::FLOAT;
		res.val.float_val = val.float_val / var.val.float_val;
	}
	else if (btype == HEMUIRBtype::DOUBLE) {
		hemu_code.err(var.btype == HEMUIRBtype::DOUBLE, "err: HEMUIRVar op '/' type err!");
		res.btype = HEMUIRBtype::DOUBLE;
		res.val.double_val = val.double_val / var.val.double_val;
	}
	else {
		hemu_code.err(0, "err: HEMUIRVar op '/' type err!");
	}
	return res;
}

HEMUIRVar HEMUIRVar::operator%(const HEMUIRVar& var) {
	HEMUIRVar res;
	if (btype == HEMUIRBtype::I32) {
		hemu_code.err(var.btype == HEMUIRBtype::I32, "err: HEMUIRVar op '%' type err!");
		res.btype = HEMUIRBtype::I32;
		res.val.i32_val = val.i32_val % var.val.i32_val;
	}
	else {
		hemu_code.err(0, "err: HEMUIRVar op '%' type err!");
	}
	return res;
}

HEMUIRVar HEMUIRVar::operator<<(const HEMUIRVar& var) {
	HEMUIRVar res;
	if (btype == HEMUIRBtype::I32) {
		hemu_code.err(var.btype == HEMUIRBtype::I32, "err: HEMUIRVar op '<<' type err!");
		res.btype = HEMUIRBtype::I32;
		res.val.i32_val = val.i32_val << var.val.i32_val;
	}
	else {
		hemu_code.err(0, "err: HEMUIRVar op '<<' type err!");
	}
	return res;
}

HEMUIRVar HEMUIRVar::operator&(const HEMUIRVar& var) {
	HEMUIRVar res;
	if (btype == HEMUIRBtype::I32) {
		hemu_code.err(var.btype == HEMUIRBtype::I32, "err: HEMUIRVar op '&' type err!");
		res.btype = HEMUIRBtype::I32;
		res.val.i32_val = val.i32_val & var.val.i32_val;
	}
	else if (btype == HEMUIRBtype::I8) {
		hemu_code.err(var.btype == HEMUIRBtype::I8, "err: HEMUIRVar op '&' type err!");
		hemu_code.err(var.val.i8_val == 0 || var.val.i8_val == 1, "err: HEMUIRVar op '&' type err!");
		hemu_code.err(val.i8_val == 0 || val.i8_val == 1, "err: HEMUIRVar op '&' type err!");
		res.btype = HEMUIRBtype::I8;
		res.val.i8_val = val.i8_val & var.val.i8_val;
	}
	else {
		hemu_code.err(0, "err: HEMUIRVar op '&' type err!");
	}
	return res;
}

HEMUIRVar HEMUIRVar::operator|(const HEMUIRVar& var) {
	HEMUIRVar res;
	if (btype == HEMUIRBtype::I32) {
		hemu_code.err(var.btype == HEMUIRBtype::I32, "err: HEMUIRVar op '|' type err!");
		res.btype = HEMUIRBtype::I32;
		res.val.i32_val = val.i32_val | var.val.i32_val;
	}
	else if (btype == HEMUIRBtype::I8) {
		hemu_code.err(var.btype == HEMUIRBtype::I8, "err: HEMUIRVar op '|' type err!");
		hemu_code.err(var.val.i8_val == 0 || var.val.i8_val == 1, "err: HEMUIRVar op '|' type err!");
		hemu_code.err(val.i8_val == 0 || val.i8_val == 1, "err: HEMUIRVar op '|' type err!");
		res.btype = HEMUIRBtype::I8;
		res.val.i8_val = val.i8_val | var.val.i8_val;
	}
	else {
		hemu_code.err(0, "err: HEMUIRVar op '|' type err!");
	}
	return res;
}

HEMUIRVar HEMUIRVar::operator^(const HEMUIRVar& var) {
	HEMUIRVar res;
	if (btype == HEMUIRBtype::I32) {
		hemu_code.err(var.btype == HEMUIRBtype::I32, "err: HEMUIRVar op '^' type err!");
		res.btype = HEMUIRBtype::I32;
		res.val.i32_val = val.i32_val ^ var.val.i32_val;
	}
	else if (btype == HEMUIRBtype::I8) {
		hemu_code.err(var.btype == HEMUIRBtype::I8, "err: HEMUIRVar op '^' type err!");
		hemu_code.err(var.val.i8_val == 0 || var.val.i8_val == 1, "err: HEMUIRVar op '^' type err!");
		hemu_code.err(val.i8_val == 0 || val.i8_val == 1, "err: HEMUIRVar op '^' type err!");
		res.btype = HEMUIRBtype::I8;
		res.val.i8_val = val.i8_val ^ var.val.i8_val;
	}
	else {
		hemu_code.err(0, "err: HEMUIRVar op '^' type err!");
	}
	return res;
}

void HEMUIRVar::print_var() {
	if (btype == HEMUIRBtype::I32) {
		std::cout << "type: I32" << std::endl;
		std::cout << "value: " << val.i32_val << std::endl;
	}
	else if (btype == HEMUIRBtype::I8) {
		std::cout << "type: I8" << std::endl;
		std::cout << "value: " << (int32_t)val.i8_val << std::endl;
	}
	else if (btype == HEMUIRBtype::FLOAT) {
		std::cout << "type: FLOAT" << std::endl;
		std::cout << "value: " << val.float_val << std::endl;
	}
	else if (btype == HEMUIRBtype::DOUBLE) {
		std::cout << "type: DOUBLE" << std::endl;
		std::cout << "value: " << val.double_val << std::endl;
	}
	else if (btype == HEMUIRBtype::POINTER) {
		std::cout << "type: POINTER" << std::endl;
		std::cout << "value: ";
		std::cout << "0x";
		std::cout << std::hex << val.i64_val << std::endl;
		std::cout << std::dec;
	}
	else
		std::cout << "err var base type!" << std::endl;
}

bool HEMUIROpnd::valid_opnd() {
	bool res = true;
	if (tp == HEMUIROpnd::TEMP &&
		(temp_idx >= func_inst_tb.get_fins_top().temp_tb.get_size()))
		res = false;
	else if (tp == HEMUIROpnd::GLOVAR && glovar_idx == SIZE_MAX)
		res = false;
	return res;
}

/* get glo
 * return Glovar
 */
HEMUIRGlovar HEMUIRGloTb::get_glo(size_t idx) {
	HEMUIRGlovar glovar;
	if (idx < glo_tb.size())
		return glo_tb.at(idx);
	else {
		hemu_code.err(0, "err: get_glo idx >= glo_tb.size()!");
		return glovar;
	}
}

/* add glo tb
 * return idx
 */
size_t HEMUIRGloTb::add_glo_tb(HEMUIRGlovar glo_var) {
	glo_tb.push_back(glo_var);
	return glo_tb.size() - 1;
}

/* search glo tb by name
 * return idx
 */
size_t HEMUIRGloTb::search_glo(std::string name) {
	for (size_t cnt = 0; cnt < glo_tb.size(); ++cnt) {
		if (glo_tb.at(cnt).glo_name == name) {
			return cnt;
		}
	}
	return SIZE_MAX;
}

/* get temp val
 * temp_idx -> idx, HEMUIRVar var -> var
 * return val
 * btype = HEMUIRBtype::UNKNOWN when temp_idx >= TempTb.size();
 */
HEMUIRVar HEMUIRTempTb::get_temp_val(size_t temp_idx) {
	HEMUIRVar val;
	if (temp_idx < temp_tb.size())
		val = temp_tb.at(temp_idx);
	else
		hemu_code.err(0, "err: get_temp_val temp_idx >= TempTb.size()!");
	return val;
}

size_t HEMUIRTempTb::get_size() {
	return temp_tb.size();
}


/* set temp tb
 * temp_idx -> idx, HEMUIRVar var -> var
 * check tp and pad
 * return idx
 */
size_t HEMUIRTempTb::set_temp_tb(size_t temp_idx, const HEMUIRVar& var) {
	if (temp_idx > temp_tb.size()) {
		HEMUIRVar pad;

		while (temp_idx > temp_tb.size()) {
			temp_tb.push_back(pad);
		}
		temp_tb.push_back(var);
		return temp_idx;
	}
	else if (temp_idx == temp_tb.size()) {
		temp_tb.push_back(var);
		return temp_idx;
	}
	else {
		temp_tb.at(temp_idx) = var;
		return temp_idx;
	}
}

/* add func tb
 * return idx
 */
size_t HEMUIRFTb::add_func(const HEMUIRFEntry& func) {
	func_tb.push_back(func);
	return func_tb.size() - 1;
}

/* search func_tb tb by name
 * return idx
 * SIZE_MAX when not found
 */
size_t HEMUIRFTb::search_idx(std::string fname) {
	for (size_t cnt = 0; cnt < func_tb.size(); ++cnt) {
		if (func_tb.at(cnt).name == fname) {
			return cnt;
		}
	}
	return SIZE_MAX;
}

/* get func_tb by idx
 * return idx
 */
HEMUIRFEntry HEMUIRFTb::get_fe(size_t idx) {
	HEMUIRFEntry fent;
	if (idx < func_tb.size())
		fent = func_tb.at(idx);
	else
		hemu_code.err(0, "err: get_fe idx >= func_tb.size()!");
	return fent;

}

/*
 * get size of func ins
 */
size_t HEMUIRFInsTb::fins_size() {
	return fun_ins.size();
}

/*
 * get top func ins
 */
HEMUIRFIns HEMUIRFInsTb::get_fins_top() {
	HEMUIRFIns fins;
	if (fins_size() > 0)
		fins = fun_ins.at(fun_ins.size() - 1);
	else
		hemu_code.err(0, "err: get_fins_top fins_size = 0");
	return fins;
}

HEMUIRFIns HEMUIRFInsTb::get_fins_next() {
	HEMUIRFIns fins;
	if (fins_size() > 1)
		fins = fun_ins.at(fun_ins.size() - 2);
	else
		hemu_code.err(0, "err: get_fins_top fins_size <= 1");
	return fins;

}

/*
 * push func ins
 */
size_t HEMUIRFInsTb::push_func_ins(const HEMUIRFIns& func_ins) {
	fun_ins.push_back(func_ins);
	return fun_ins.size() - 1;
}

/*
 * pop func ins
 */
bool HEMUIRFInsTb::pop_func_ins() {
	fun_ins.pop_back();
	return true;
}


/*
 * get cur func ins var
 */
HEMUIRVar HEMUIRFInsTb::get_cur_fins_var(const HEMUIROpnd& opnd) {
	if (opnd.tp == HEMUIROpnd::IMM) {
		return opnd.imm;
	}
	else if (opnd.tp == HEMUIROpnd::GLOVAR) {
		HEMUIRVar var;
		var.btype = HEMUIRBtype::POINTER;
		//var.val.ptr_val = glo_tb.glo_tb.at(opnd.glovar_idx).glo_var_ptr;
		var.val.ptr_val = glo_tb.get_glo(opnd.glovar_idx).glo_var_ptr;
		return var;
	}
	else {
		//return fun_ins.at(fun_ins.size() - 1).temp_tb.temp_tb.at(opnd.temp_idx);
		return get_fins_top().temp_tb.get_temp_val(opnd.temp_idx);
	}
}

/*
 * set cur func ins line num
 */
bool HEMUIRFInsTb::set_cur_fins_line(size_t lineno) {
	//assert(lineno >= func_tb.func_tb.at(fun_ins.at(fun_ins.size() - 1).func_idx).lineno_start);
	hemu_code.err(lineno >= func_tb.get_fe(get_fins_top().func_idx).lineno_start, "err: set_cur_fins_line out of range!");
	//assert(lineno <= func_tb.func_tb.at(fun_ins.at(fun_ins.size() - 1).func_idx).lineno_end);
	hemu_code.err(lineno <= func_tb.get_fe(get_fins_top().func_idx).lineno_end, "err: set_cur_fins_line out of range!");
	fun_ins.at(fun_ins.size() - 1).cur_line = lineno;
	return true;
}

/*
 * set cur func ins temp_tb by temp_idx
 */
size_t HEMUIRFInsTb::set_cur_fins_temp_tb(size_t temp_idx, const HEMUIRVar& val) {
	hemu_code.err(fins_size() > 0, "err: set_cur_fins_temp_tb fins_size err!");
	return fun_ins.at(fun_ins.size() - 1).temp_tb.set_temp_tb(temp_idx, val);
	//return get_fins_top().temp_tb.set_temp_tb(temp_idx, val);
}

/*
 * set next func ins temp_tb by temp_idx
 */
size_t HEMUIRFInsTb::set_next_fins_temp_tb(size_t temp_idx, const HEMUIRVar& val) {
	hemu_code.err(fins_size() > 1, "err: set_next_fins_temp_tb fins_size err!");
	return fun_ins.at(fun_ins.size() - 2).temp_tb.set_temp_tb(temp_idx, val);
	//return get_fins_top().temp_tb.set_temp_tb(temp_idx, val);
}


/*
 * set cur func ins temp_tb by opnd
 */
size_t HEMUIRFInsTb::set_cur_fins_temp_tb(const HEMUIROpnd& opnd, const HEMUIRVar& val) {
	return set_cur_fins_temp_tb(opnd.temp_idx, val);
}

bool HEMUIRInst::exec_nop() {
	hemu_code.set_cur_line(hemu_code.cur_inst_idx + 1);
	return true;
}

bool HEMUIRInst::exec_call() {
	HEMUIRFIns fins;
	HEMUIRFEntry cfety = func_tb.get_fe(funcidx);
	fins.func_idx = funcidx;
	fins.cur_line = cfety.lineno_start;
	fins.temp_tb = cfety.temp_tb;
	fins.base_ptr = stack.top;
	fins.stack_ptr = stack.top;
	hemu_code.err(fargs.size() == cfety.fargs.size(), "err: exec_call args size err!");
	for (size_t idx = 0; idx < fargs.size(); ++idx) {
		hemu_code.err(func_inst_tb.get_cur_fins_var(fargs.at(idx)).btype
			== cfety.temp_tb.get_temp_val(cfety.fargs.at(idx).temp_idx).btype, "err: exec_call arrgs type err!");
		fins.temp_tb.set_temp_tb(cfety.fargs.at(idx).temp_idx,
			func_inst_tb.get_cur_fins_var(fargs.at(idx)));
	}
	func_inst_tb.push_func_ins(fins);
	hemu_code.set_cur_line(fins.cur_line);
	return true;
}

bool HEMUIRInst::exec_ret() {
	HEMUIRVar ret_var;
	if (value.tp == HEMUIROpnd::IMM) {
		ret_var.btype = value.imm.btype;
		ret_var.val.i64_val = value.imm.val.i64_val;
	}
	else if (value.tp == HEMUIROpnd::TEMP) {
		//ret_var = func_inst_tb.fun_ins.at(func_inst_tb.fun_ins.size() - 1).temp_tb.temp_tb.at(inst.value.temp_idx);
		ret_var = func_inst_tb.get_fins_top().temp_tb.get_temp_val(value.temp_idx);
	}
	else {
		hemu_code.err(0, "err: exec_ret value.tp err!");
	}
	//size_t call_line = func_inst_tb.fun_ins.at(func_inst_tb.fun_ins.size() - 2).cur_line;
	size_t call_line = func_inst_tb.get_fins_next().cur_line;
	//HEMUIRInst call_inst = insts.at(call_line);
	HEMUIRInst call_inst = hemu_code.get_inst(call_line);
	if (call_inst.call_dest)
		//func_inst_tb.fun_ins.at(func_inst_tb.fun_ins.size() - 2).temp_tb.set_temp_tb(call_inst.dest.temp_idx, ret_var);
		func_inst_tb.set_next_fins_temp_tb(call_inst.dest.temp_idx, ret_var);
	//stack.free(func_inst_tb.fun_ins.at(func_inst_tb.fun_ins.size() - 1).base_ptr);
	stack.free(func_inst_tb.get_fins_top().base_ptr);
	func_inst_tb.pop_func_ins();
	hemu_code.set_cur_line(call_line + 1);
	return true;
}

bool HEMUIRInst::exec_br() {
	bool br_taken = true;
	hemu_code.err(oper == HEMUIROper::BRu || oper == HEMUIROper::BRc, "err: exec_br oper err!");
	if (oper == HEMUIROper::BRc) {
		HEMUIRVar var = func_inst_tb.get_cur_fins_var(cond);
		hemu_code.err(var.btype == HEMUIRBtype::I8, "err: exec_br opnd tp err!");
		if (var.val.i8_val == 0)
			br_taken = false;
		if (br_taken == true) {
			HEMUIRVar iftrue = func_inst_tb.get_cur_fins_var(ifture);
			hemu_code.err(iftrue.btype == HEMUIRBtype::LABEL, "err: exec_br opnd tp err!");
			hemu_code.set_cur_line(iftrue.val.i64_val);

		}
		else {
			HEMUIRVar iffalse_var = func_inst_tb.get_cur_fins_var(iffalse);
			hemu_code.err(iffalse_var.btype == HEMUIRBtype::LABEL, "err: exec_br opnd tp err!");
			hemu_code.set_cur_line(iffalse_var.val.i64_val);
		}
		return true;
	}
	else {
		HEMUIRVar brdest_var = func_inst_tb.get_cur_fins_var(brdest);
		hemu_code.err(brdest_var.btype == HEMUIRBtype::LABEL, "err: exec_br opnd tp err!");
		hemu_code.set_cur_line(brdest_var.val.i64_val);
		return true;
	}
}

bool HEMUIRInst::exec_alloca() {
	HEMUIRVar dest_var;
	dest_var.btype = HEMUIRBtype::POINTER;
	dest_var.val.ptr_val = stack.salloc(type, alignment);
	func_inst_tb.set_cur_fins_temp_tb(dest, dest_var);
	hemu_code.set_cur_line(hemu_code.cur_inst_idx + 1);
	return true;
}

bool HEMUIRInst::exec_load() {
	hemu_code.err(dest.tp == HEMUIROpnd::TEMP, "err: exec_load type err!");
	HEMUIRVar ptr = func_inst_tb.get_cur_fins_var(pointer);
	hemu_code.err(ptr.btype == HEMUIRBtype::POINTER);
	hemu_code.err(valid_addr(ptr.val.ptr_val), "err: exec_load addr err!");
	func_inst_tb.set_cur_fins_temp_tb(dest, hload(ptr.val.ptr_val, type.base_type));
	hemu_code.set_cur_line(hemu_code.cur_inst_idx + 1);
	return true;
}

bool HEMUIRInst::exec_store() {
	HEMUIRVar ptr = func_inst_tb.get_cur_fins_var(pointer);
	HEMUIRVar var = func_inst_tb.get_cur_fins_var(value);
	hemu_code.err(ptr.btype == HEMUIRBtype::POINTER, "err: exec_store type err!");
	hemu_code.err(valid_addr(ptr.val.ptr_val), "err: exec_store addr err!");
	hstore(ptr.val.ptr_val, var);
	hemu_code.set_cur_line(hemu_code.cur_inst_idx + 1);
	return true;
}

bool HEMUIRInst::exec_cmp() {
	HEMUIRVar dest_var;
	hemu_code.err(type.length == 1, "err: exec_cmp type err");
	hemu_code.err(type.base_type == HEMUIRBtype::I32 || type.base_type == HEMUIRBtype::FLOAT || type.base_type == HEMUIRBtype::DOUBLE, "err: exec_cmp type err!");
	HEMUIRVal op1_val = func_inst_tb.get_cur_fins_var(op1).val;
	HEMUIRVal op2_val = func_inst_tb.get_cur_fins_var(op2).val;
	if (type.base_type == HEMUIRBtype::I32)
		dest_var = cmp_op(op1_val.i32_val, op2_val.i32_val, cmpcond);
	else if (type.base_type == HEMUIRBtype::FLOAT)
		dest_var = cmp_op(op1_val.float_val, op2_val.float_val, cmpcond);
	else if (type.base_type == HEMUIRBtype::DOUBLE)
		dest_var = cmp_op(op1_val.double_val, op2_val.double_val, cmpcond);
	func_inst_tb.set_cur_fins_temp_tb(dest, dest_var);
	hemu_code.set_cur_line(hemu_code.cur_inst_idx + 1);
	return true;
}

bool HEMUIRInst::exec_fneg() {
	HEMUIRVar dest_var;
	hemu_code.err(type.length == 1, "err: exec_fneg type err!");
	hemu_code.err(type.base_type == HEMUIRBtype::FLOAT || type.base_type == HEMUIRBtype::DOUBLE, "err: exec_fneg type err!");
	HEMUIRVar op1_val = func_inst_tb.get_cur_fins_var(op1);
	hemu_code.err(type.base_type == op1_val.btype, "err: exec_fneg type err!");
	if (type.base_type == HEMUIRBtype::FLOAT) {
		dest_var.btype = HEMUIRBtype::FLOAT;
		dest_var.val.float_val = fneg_op(op1_val.val.float_val);
	}
	else {
		dest_var.btype = HEMUIRBtype::DOUBLE;
		dest_var.val.double_val = fneg_op(op1_val.val.double_val);
	}
	func_inst_tb.set_cur_fins_temp_tb(dest, dest_var);
	hemu_code.set_cur_line(hemu_code.cur_inst_idx + 1);
	return true;
}

bool HEMUIRInst::exec_calc() {
	HEMUIRVar dest_var;
	dest_var.btype = type.base_type;
	hemu_code.err(type.length == 1, "err: exec_calc type err!");
	HEMUIRVar op1_var = func_inst_tb.get_cur_fins_var(op1);
	HEMUIRVar op2_var = func_inst_tb.get_cur_fins_var(op2);
	if (oper == HEMUIROper::ADD && type.base_type == HEMUIRBtype::POINTER)
		hemu_code.err(op1_var.btype == HEMUIRBtype::POINTER && op2_var.btype == HEMUIRBtype::I32, "err: exec_calc type err");
	else
		hemu_code.err(op1_var.btype == type.base_type && op2_var.btype == type.base_type);

	if (oper == HEMUIROper::ADD || oper == HEMUIROper::FADD)
		dest_var = op1_var + op2_var;
	else if (oper == HEMUIROper::SUB || oper == HEMUIROper::FSUB)
		dest_var = op1_var - op2_var;
	else if (oper == HEMUIROper::MUL || oper == HEMUIROper::FMUL)
		dest_var = op1_var * op2_var;
	else if (oper == HEMUIROper::FDIV || oper == HEMUIROper::SDIV || oper == HEMUIROper::UDIV)
		dest_var = op1_var / op2_var;
	else if (oper == HEMUIROper::SREM || oper == HEMUIROper::UREM)
		dest_var = op1_var % op2_var;
	else if (oper == HEMUIROper::SHL)
		dest_var = op1_var << op2_var;
	else if (oper == HEMUIROper::AND)
		dest_var = op1_var & op2_var;
	else if (oper == HEMUIROper::OR)
		dest_var = op1_var | op2_var;
	else if (oper == HEMUIROper::XOR)
		dest_var = op1_var ^ op2_var;
	else {
		hemu_code.err(oper == HEMUIROper::LSHR || oper == HEMUIROper::ASHR, "err: exec_calc oper err!");
		dest_var.btype = HEMUIRBtype::I32;
		hemu_code.err(op1_var.btype == HEMUIRBtype::I32 && op2_var.btype == HEMUIRBtype::I32, "err: exec_calc type err!");
		if (oper == HEMUIROper::ASHR)
			dest_var.val.i32_val = (uint32_t)(op1_var.val.i32_val) >> (uint32_t)(op2_var.val.i32_val);
		else
			dest_var.val.i32_val = op1_var.val.i32_val >> op2_var.val.i32_val;

	}
	func_inst_tb.set_cur_fins_temp_tb(dest, dest_var);
	hemu_code.set_cur_line(hemu_code.cur_inst_idx + 1);
	return true;

}


/* exec line
 * set run_status to 0 when last main ret
 */
bool HEMUIRInst::exec_line() {
	if (hdb_break)
		hemu_code.hdb();
	if (oper == HEMUIROper::CALL) {
		if (func_name == "print_int") {
			HEMUIRVar tp = func_inst_tb.get_cur_fins_var(fargs.at(0));
			hemu_code.err(tp.btype == HEMUIRBtype::I32, "err: exec_line type err!");
			std::cout << tp.val.i32_val << std::endl;
			hemu_code.set_cur_line(hemu_code.cur_inst_idx + 1);
		}
		else if (func_name == "print_double") {
			HEMUIRVar tp = func_inst_tb.get_cur_fins_var(fargs.at(0));
			hemu_code.err(tp.btype == HEMUIRBtype::DOUBLE, "err: exec_line type err!");
			std::cout << tp.val.double_val << std::endl;
			hemu_code.set_cur_line(hemu_code.cur_inst_idx + 1);
		}
		else if (func_name == "print_float") {
			HEMUIRVar tp = func_inst_tb.get_cur_fins_var(fargs.at(0));
			hemu_code.err(tp.btype == HEMUIRBtype::FLOAT, "err: exec_line type err!");
			std::cout << tp.val.float_val << std::endl;
			hemu_code.set_cur_line(hemu_code.cur_inst_idx + 1);
		}
		else if (func_name == "print_bool") {
			HEMUIRVar tp = func_inst_tb.get_cur_fins_var(fargs.at(0));
			hemu_code.err(tp.btype == HEMUIRBtype::I8, "err: exec_line type err!");
			//std::cout << tp.val.i8_val << std::endl;
			if (tp.val.i8_val)
				std::cout << "true" << std::endl;
			else
				std::cout << "false" << std::endl;

			hemu_code.set_cur_line(hemu_code.cur_inst_idx + 1);
		}
		else if (func_name == "get_int") {
			HEMUIRVar var;
			var.btype = HEMUIRBtype::I32;
			std::cin >> var.val.i32_val;
			func_inst_tb.set_cur_fins_temp_tb(dest, var);
			hemu_code.set_cur_line(hemu_code.cur_inst_idx + 1);
		}
		else if (func_name == "get_float") {
			HEMUIRVar var;
			var.btype = HEMUIRBtype::FLOAT;
			std::cin >> var.val.float_val;
			func_inst_tb.set_cur_fins_temp_tb(dest, var);
			hemu_code.set_cur_line(hemu_code.cur_inst_idx + 1);
		}
		else if (func_name == "get_double") {
			HEMUIRVar var;
			var.btype = HEMUIRBtype::DOUBLE;
			std::cin >> var.val.double_val;
			func_inst_tb.set_cur_fins_temp_tb(dest, var);
			hemu_code.set_cur_line(hemu_code.cur_inst_idx + 1);
		}
		else {
			hemu_code.err(funcidx != SIZE_MAX, "err: exec_line funcidx err!");
			exec_call();
		}

	}
	else if (oper == HEMUIROper::NOP) {
		exec_nop();
	}
	else if (oper == HEMUIROper::RET) {
		//if (func_inst_tb.fun_ins.size() == 1)
		if (func_inst_tb.fins_size() == 1)
			hemu_code.run_status = false;
		else
			exec_ret();
	}
	else if (oper == HEMUIROper::BRc || oper == HEMUIROper::BRu)
		exec_br();
	else if (oper == HEMUIROper::ALLOCA)
		exec_alloca();
	else if (oper == HEMUIROper::LOAD)
		exec_load();
	else if (oper == HEMUIROper::STORE)
		exec_store();
	else if (oper == HEMUIROper::FCMP || oper == HEMUIROper::ICMP)
		exec_cmp();
	else if (oper == HEMUIROper::FNEG)
		exec_fneg();
	else
		exec_calc();

	return true;
}



/*
 * deal with input
 * output insts
 */
HEMUIRCode::HEMUIRCode(std::istream& stream, bool debug) {
	size_t line_no = 0;
	std::string line;
	size_t func_idx = 0;
	hemu_hdb = debug;
	hemu_err = false;

	while (std::getline(stream, line)) {
		HEMUIRInst inst;
		std::vector<std::string> toks;

		rm_comm(line);
		parse(line, lr_delim, toks);

		if (toks.size() == 0) {
			inst.oper = HEMUIROper::NOP;
		}
		else if (std::isdigit(toks.at(0).at(0))) {
			HEMUIRVar label;
			label.btype = HEMUIRBtype::LABEL;
			label.val.i64_val = line_no;
			size_t temp_idx = std::stoll(toks.at(0));
			func_tb.func_tb.at(func_idx).temp_tb.set_temp_tb(temp_idx, label);
			inst.oper = HEMUIROper::NOP;
		}
		else if (toks.at(0) == "}") {
			func_tb.func_tb.at(func_idx).lineno_end = line_no;
			++func_idx;
			inst.oper = HEMUIROper::NOP;
		}
		else if (toks.at(0) == "define") {
			HEMUIRFEntry fent;
			fent.name = toks.at(3).substr(1);
			fent.lineno_start = line_no;
			fent.ret_tp = get_btype(toks.at(2));
			set_fargs(toks, 4, fent.fargs);
			for (size_t idx = 0; idx < fent.fargs.size(); ++idx) {
				//fent.temp_tb.temp_tb.at(fent.fargs.at(idx).temp_idx).btype = get_btype(toks.at(4 + idx * 2));
				HEMUIRVar var;
				var.btype = get_btype(toks.at(4 + idx * 2));
				fent.temp_tb.set_temp_tb(fent.fargs.at(idx).temp_idx, var);
			}
			func_tb.add_func(fent);
			inst.oper = HEMUIROper::NOP;
		}
		else if (toks.at(0).at(0) == '@') {
			HEMUIRGlovar glov;
			size_t align;
			glov.glo_name = toks.at(0).substr(1);
			size_t tp_off = set_type(toks, 4, glov.tp);
			if (toks.at(4 + tp_off) == "zeroinitializer") {
				align = std::stoll(toks.at(6 + tp_off));
				glov.glo_var_ptr = gmem.galloc(glov.tp, align);
				hstore(glov.glo_var_ptr, glov.tp, true);
				glo_tb.add_glo_tb(glov);
			}
			else {
				if (glov.tp.length == 1) {
					HEMUIRVar var;
					var.btype = glov.tp.base_type;
					set_imm_val(toks, 5, var);
					align = std::stoll(toks.at(7));
					glov.glo_var_ptr = gmem.galloc(glov.tp, align);
					hstore(glov.glo_var_ptr, var);
				}
				else {
					HEMUIRVar var;
					std::vector<HEMUIRVar> vars;
					var.btype = glov.tp.base_type;
					align = std::stoll(toks.at(tp_off + 2 * glov.tp.length + 5));
					for (size_t idx = 0; idx < glov.tp.length; ++idx) {
						set_imm_val(toks, 2 * idx + 8, var);
						vars.push_back(var);
					}
					glov.glo_var_ptr = gmem.galloc(glov.tp, align);
					hstore(glov.glo_var_ptr, glov.tp, vars);
				}
				glo_tb.add_glo_tb(glov);
			}
			inst.oper = HEMUIROper::NOP;
		}
		else if (toks.at(0) == "ret")
			op_ret_ir(toks, inst);
		else if (toks.at(0) == "br")
			op_br_ir(toks, inst);
		else if (toks.at(0) == "store")
			op_store_ir(toks, inst);
		else if (toks.at(0) == "call")
			op_call_ir(toks, inst);
		else if (toks.at(2).substr(1) == "cmp")
			op_cmp_ir(toks, inst);
		else if (toks.at(2) == "alloca")
			op_alloc_ir(toks, inst);
		else if (toks.at(2) == "load")
			op_load_ir(toks, inst);
		else if (toks.at(2) == "load")
			op_fneg_ir(toks, inst);
		else if (toks.at(2) == "call")
			op_call_ir(toks, inst);
		else
			op_calc_ir(toks, inst);
		inst.hdb_break = false;
		insts.push_back(inst);
		++line_no;
	}
	cur_inst_idx = func_tb.func_tb.at(func_tb.search_idx("main")).lineno_start;

}

/*
 * get inst
 */
HEMUIRInst HEMUIRCode::get_inst(size_t idx) {
	HEMUIRInst inst;
	if (idx < insts.size())
		inst = insts.at(idx);
	else
		hemu_code.err(0, "err: get_inst idx out of range!");
	return inst;
}

/*
 * set insts and cur func ins  cur line
 */
bool HEMUIRCode::set_cur_line(size_t lineno) {
	assert(lineno >= 0);
	assert(lineno < insts.size());
	cur_inst_idx = lineno;
	func_inst_tb.set_cur_fins_line(lineno);
	return true;
}


HEMUIRVar HEMUIRCode::executor(bool debug) {
	run_status = true;
	HEMUIRInst start;
	start.hdb_break = false;
	start.oper = HEMUIROper::CALL;
	start.call_dest = false;
	start.func_name = "main";
	start.funcidx = func_tb.search_idx(start.func_name);
	insts.at(func_tb.func_tb.at(func_tb.search_idx("main")).lineno_start + 1).hdb_break = debug;
	start.exec_line();
	while (run_status)
		insts.at(cur_inst_idx).exec_line();
	return func_inst_tb.get_cur_fins_var(insts.at(cur_inst_idx).value);
}

void HEMUIRCode::err(bool ass, const char* info) {
	if (ass == false) {
		if (info != nullptr)
			std::cout << info << std::endl;
		std::cout << "err line num: " << cur_inst_idx + 1 << std::endl;
		hemu_err = true;
		hdb();
		return;
	}
	return;
}


const std::string hdb_delim = " ";

/*
 * HEMU debug
 */
void HEMUIRCode::hdb() {
	std::string line;
	const std::string hdb_info = "(hdb)";
	const std::string err_info = "(err)";
	std::cout << hdb_info;
	if (hemu_err == true)
		std::cout << err_info;
	std::cout << " ";
	while (std::getline(std::cin, line)) {
		std::vector<std::string>toks;
		parse(line, hdb_delim, toks);
		if (toks.size() > 0) {
			if ((toks.at(0) == "b" || toks.at(0) == "break") && hemu_err == false) {
				hdb_break(toks);
			}
			else if (toks.at(0) == "p" || toks.at(0) == "print") {
				hdb_print(toks);
			}
			//else if (toks.size() == 2 && toks.at(0).at(0) == 'x' && toks.at(1).at(0) == '0' && toks.at(1).at(1) == 'x') {
			else if (toks.size() == 2 && toks.at(0).at(0) == 'x') {
				hdb_x(toks);
			}
			else if (toks.at(0) == "list" || toks.at(0) == "l") {
				hdb_list(toks);
			}
			else if ((toks.at(0) == "c" || toks.at(0) == "continue") && hemu_err == false) {
				return;
			}
			// TODO: step, next, print mem, list...
			else
				std::cout << "err command" << std::endl;
		}
		std::cout << hdb_info;
		if (hemu_err == true)
			std::cout << err_info;
		std::cout << " ";

	}
}

void HEMUIRCode::hdb_break(const std::vector<std::string>& toks) {
	if (toks.size() == 2 && isdigit(toks.at(1).at(0))) {
		size_t line_num = std::stoll(toks.at(1));
		if (line_num <= insts.size() && line_num >= 1ul) {
			insts.at(line_num - 1).hdb_break = true;
			std::cout << "Breakpoint at line:" << line_num << std::endl;
		}
		else
			std::cout << "out of range" << std::endl;
	}
	else
		std::cout << "args err" << std::endl;
}

void HEMUIRCode::hdb_print(const std::vector<std::string>& toks) {
	if (toks.size() == 2 && (toks.at(1).at(0) == '@' || toks.at(1).at(0) == '%')) {
		HEMUIROpnd opnd;
		bool b1 = set_opnd(toks, 1, HEMUIRBtype::UNKNOWN, opnd);
		bool b2 = opnd.valid_opnd();
		HEMUIRVar var;
		if (b2)
			var = func_inst_tb.get_cur_fins_var(opnd);
		if (b1 && b2) {
			var.print_var();
		}
		else
			std::cout << "err opnd" << std::endl;
	}
	else
		std::cout << "args err" << std::endl;
}

void HEMUIRCode::hdb_x(const std::vector<std::string>& toks) {
	HEMUIRVar var;
	bool valid = true;
	if (toks.at(0) == "x/i32" || toks.at(0) == "x/i8" || toks.at(0) == "x/double" || toks.at(0) == "x/float") {
		void* addr = nullptr;
		if (toks.at(1).substr(0, 2) == "0x") {
			addr = (void*)std::stoull(toks.at(1).substr(2), nullptr, 16);
		}
		else if (toks.at(1).substr(0, 1) == "%" || toks.at(1).substr(0, 1) == "@") {
			HEMUIROpnd opnd;
			bool b1 = set_opnd(toks, 1, HEMUIRBtype::UNKNOWN, opnd);
			bool b2 = opnd.valid_opnd();
			HEMUIRVar var;
			if (b2)
				var = func_inst_tb.get_cur_fins_var(opnd);
			if (b1 && b2 && var.btype == HEMUIRBtype::POINTER) {
				addr = var.val.ptr_val;
			}
			else {
				std::cout << "err opnd" << std::endl;
				valid = false;
			}
		}
		else {
			std::cout << "the addr prefix must be 0x or % or @" << std::endl;
			valid = false;
		}
		if (valid) {
			var.btype = get_btype(toks.at(0).substr(2));
			if (valid_addr((char*)addr)) {
				if (var.btype == HEMUIRBtype::I32)
					var.val.i32_val = *(int32_t*)addr;
				else if (var.btype == HEMUIRBtype::I8)
					var.val.i8_val = *(int8_t*)addr;
				else if (var.btype == HEMUIRBtype::DOUBLE)
					var.val.double_val = *(double*)addr;
				else if (var.btype == HEMUIRBtype::FLOAT)
					var.val.float_val = *(float*)addr;
				var.print_var();
			}
			else
				std::cout << "addr invalid" << std::endl;
		}
	}
	else
		std::cout << "err type" << std::endl;
}

void HEMUIRCode::hdb_list(const std::vector<std::string>& toks) {
	if (toks.size() <= 1ul || toks.at(1) == "cur_line")
		std::cout << "current line number: " << cur_inst_idx + 1 << std::endl;
	else
		std::cout << "err command" << std::endl;
}

